<!--
  @desc 日期时间范围选择器，支持添加快捷按钮设置时间范围
  @author 石艳军
  @date 2023-06-28
  使用方式:
    <base-time-range-picker
      v-model="timeRange"
      :shortcuts="[{ value: '1d', label: '近1天' }, { value: '7d', label: '近7天' }]"
      size="mini"
      effect="dark"
      :default-time-range="defaultTimeRange"
      :must-check="false"
      @pick="onTimeRangePick"
    ></base-time-range-picker>
-->
<template>
  <div class="time-range-picker" :class="[`time-range-picker--${size}`, `time-range-picker--${effect}`]">
    <el-date-picker
      v-model="timeRange"
      type="datetimerange"
      :popper-class="popperClassName"
      range-separator="至"
      value-format="yyyy-MM-dd HH:mm:ss"
      :format="labelFormat"
      start-placeholder="开始日期"
      end-placeholder="结束日期"
      align="right"
      :size="size"
      @change="onDatePickerChange"
    ></el-date-picker>

    <div class="shortcuts-wrap" v-if="shortcutList.length > 0" ref="shortcutRef">
      <div
        class="shortcuts-item"
        :class="{ 'is-active': selectedShortcutValue === item.source }"
        :style="{ width: shortcutItemWidth }"
        v-for="(item, index) in shortcutList"
        :key="index"
        :data-value="item.value"
        @click="onShortcutClick(item)"
      >{{ item.name }}</div>
    </div>
  </div>
</template>

<script>
import moment from 'moment';

const shortcutsFormatter = {
  h: '小时',
  d: '天',
  w: '周',
  M: '个月',
};

export default {
  name: 'base-time-range-picker',
  model: {
    prop: 'rangeValue',
    event: 'change',
  },
  props: {
    rangeValue: {
      type: Array,
    },
    // 默认时间范围
    defaultTimeRange: {
      type: Array,
    },
    labelFormat: {
      type: String,
      default: 'yy-MM-dd HH:mm',
    },
    // 尺寸大小
    size: {
      type: String,
      default: 'medium',  // medium | small | mini
    },
    // 主题色
    effect: {
      type: String,
      default: 'light', // dark | light
    },
    // 快捷键列表, 支持的数据结构如下：
    //   第一种: ['1h', '1d', '7d']
    //   第二种: [{ value: '1h', label: '近1小时' }, { value: '1d', label: '近1天' }, { value: '7d', label: '近7天' }]
    shortcuts: {
      type: Array,
      default: () => ['1h', '1d', '7d'], // h - 小时; d - 天; w - 周
    },
    // 默认选中的快捷项
    defaultShortcut: {
      type: String,
    },
    // 是否必须选择一个快捷日期
    mustCheck: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      timeRange: null,
      selectedShortcutValue: null,
      shortcutItemWidth: '',
    };
  },
  watch: {
    rangeValue: {
      handler(range) {
        this.timeRange = range || null;
      },
      immediate: true,
    },
    // 监听外部更新主题色，更新popper-class
    effect(theme) {
      const prevClassName = {
        dark: 'time-range-picker-popper--light',
        light: 'time-range-picker-popper--dark',
      };
      if (!prevClassName[theme]) return;
      const timeRangePickerEl = document.getElementsByClassName(prevClassName[theme]);
      if (timeRangePickerEl.length > 0) {
        timeRangePickerEl[0].classList.replace(prevClassName[theme], `time-range-picker-popper--${theme}`);
      }
    },
    defaultShortcut(val) {
      if (val) {
        this.selectedShortcutValue = val;
      }
    },
  },
  computed: {
    popperClassName() {
      return `time-range-picker-popper--${this.effect}`;
    },

    shortcutList() {
      if (!Array.isArray(this.shortcuts) || this.shortcuts.length === 0) return [];
      return this.shortcuts.map(item => {
        const result = {
          unit: '',
          name: '',
          value: '',
          source: '',
        };

        if (typeof item === 'string') {
          result.source = item;
          // 判断传入的快捷键是否是有效值
          if (!this.shortcutValid(item)) {
            result.name = item;
          } else {
            result.unit = item.slice(-1);  // 取字符串中的最后一个字符作为单位
            result.name = `${item.slice(0, -1)}${shortcutsFormatter[result.unit]}`;
            result.value = item.slice(0, -1);
          }
        } else if (Object.prototype.toString.call(item) === '[object Object]') {
          result.source = item.value;
          // 判断传入的快捷键是否是有效值
          if (!this.shortcutValid(item.value)) {
            result.name = item.label || item.value;
          } else {
            result.unit = item.value.slice(-1);
            result.name = item.label || `${item.value.slice(0, -1)}${shortcutsFormatter[result.unit]}`;
            result.value = item.value.slice(0, -1);
          }
        }
        return result;
      });
    },
  },
  mounted() {
    this.resetTime();
    this.setShortcutItemWidth();
  },
  methods: {
    /**
     * 重置日期时间及选中状态
     */
    resetTime() {
      if (this.shortcutList.length > 0 && this.mustCheck) {
        this.selectedShortcutValue = this.defaultShortcut || this.shortcutList[0].source;
      } else {
        this.selectedShortcutValue = null;
      }

      if (Array.isArray(this.defaultTimeRange) && this.defaultTimeRange.length > 0) {
        this.timeRange = this.defaultTimeRange;
        this.$emit('update:rangeValue', this.timeRange);
        this.$emit('change', this.timeRange);
      }
    },

    /**
     * 设置快捷按钮宽度
     */
    setShortcutItemWidth() {
      const shortcutEl = this.$refs.shortcutRef;
      if (!shortcutEl) return;
      const nodeWidthList = [];
      shortcutEl.childNodes.forEach(node => {
        nodeWidthList.push(node.offsetWidth);
      });
      // 将按钮中最大宽度 + 左右各6px的内边距(即12px) = 快捷按钮的宽度
      this.shortcutItemWidth = Math.max(...nodeWidthList) + 12 + 'px';
    },

    /**
     * 传入的快捷键是否为有效日期时间
     * @param { string } shortcutItem
     * @return {boolean}
     */
    shortcutValid(shortcutItem) {
      return Object.keys(shortcutsFormatter).some(o => shortcutItem.length > 1 && shortcutItem.slice(-1) === o && !Number.isNaN(Number(shortcutItem.slice(0, -1))));
    },

    /**
     * 选择器回调
     */
    onDatePickerChange() {
      this.selectedShortcutValue = null;
      this.$emit('update:rangeValue', this.timeRange || []);
      this.$emit('change', this.timeRange || []);
    },

    /**
     * 获取时间范围
     * @param {string|number} amount
     * @param unit 单位: h-小时; d-天; w-周; M-月
     */
    getTimeRange(amount, unit = 'd') {
      return {
        startTime: moment().subtract(amount, unit).format('YYYY-MM-DD HH:mm:ss'),
        endTime: moment().format('YYYY-MM-DD HH:mm:ss'), // 结束时间默认为当前时间
      };
    },

    /**
     * 快捷键点击事件回调
     */
    onShortcutClick(record) {
      // 传入无效的快捷时间
      if (!this.shortcutValid(record.source)) {
        this.$emit('pick', { type: 'other', source: record.source });
        return;
      }

      if (!this.mustCheck && this.selectedShortcutValue === record.source) {
        this.selectedShortcutValue = null;
      } else {
        this.selectedShortcutValue = record.source;
      }

      const pickResult = { type: 'time', source: this.selectedShortcutValue };
      if (this.selectedShortcutValue) {
        const range = this.getTimeRange(record.value, record.unit);
        pickResult.startTime = range.startTime;
        pickResult.endTime = range.endTime;
        this.timeRange = [pickResult.startTime, pickResult.endTime];
      } else {
        this.timeRange = this.defaultTimeRange || [];
        if (this.timeRange.length > 0) {
          pickResult.startTime = this.timeRange[0];
          pickResult.endTime = this.timeRange[1];
        }
      }

      this.$emit('update:rangeValue', this.timeRange);
      this.$emit('change', this.timeRange);
      this.$emit('pick', pickResult);
    },
  },
};
</script>

<style lang="scss">
.time-range-picker-popper {
  &--dark {
    border: 1px solid #00acfc;

    .el-picker-panel {
      &__body, &__footer {
        background-color: rgba(3, 22, 45, $alpha: 1.0);
        color: #fff;
      }
    }

    .el-time-panel {
      background-color: rgba(3, 22, 45, $alpha: 1.0) ;
      color: #fff;

      .el-time-spinner__item {
        color: #fff;

        &:hover:not(.disabled):not(.active){
          color: #000;
        }

        &.active {
          color: #00acfc;
        }
      }

      &__footer {
        .el-time-panel__btn {
          color: #fff;

          &.confirm {
            color: #00acfc;
          }
        }
      }
    }

    .el-date-range-picker__header, .el-picker-panel__icon-btn, .el-icon-arrow-right {
      color: #fff;
    }

    .el-picker-panel__footer {
      border-color: #00acfc;

      .el-button {
        color: #fff;
        border: 1px solid #00acfc;
        width: 61px;
        height: 32px;
        border-image: linear-gradient(180deg, #254B79, #358CD9) 10 10;
        background: linear-gradient(0deg, #3B93E4 0%, #0E2747 100%);
      }
    }

    .el-input {
      .el-input__inner {
        background: rgba(2, 22, 47, 0.8);
        border: 1px solid #00acfc;
        border-radius: 4px;
        color: #FFF;
      }

      &.is-disabled {
        .el-input__inner{
          background: rgba(2, 22, 47, 0.8);
          border: 1px solid #00acfc;
          color: #C0C4CC;
        }
      }
    }

    .el-date-table {
      th {
        color: #fff;
      }

      td.in-range {
        color: #1d222a;
      }
    }
  }
}
</style>

<style lang="scss" scoped>
$height: 36px;

// 选择器尺寸函数
@mixin pickerSizeMixin($sizeType) {
  @if ($sizeType == 'medium') {
    height: $height;
    line-height: $height;
    font-size: 14px;
  }
  @else if ($sizeType == 'small') {
    height: $height - 4px;
    line-height: $height - 4px;
    font-size: 13px;
  }
  @else if ($sizeType == 'mini') {
    height: $height - 8px;
    line-height: $height - 8px;
    font-size: 12px;
  }
}

// 选择器主题色函数
@mixin pickerEffectMixin($effectType) {
  @if ($effectType == 'light') {
    .shortcuts-item {
      border: 1px solid #d6dae3;
      color: #1d222a;

      &.is-active {
        color: #00acfc;
        background: rgba(36, 149, 255, 0.1);
        border: 1px solid #00acfc !important;
      }
    }
  }
  @else if ($effectType == 'dark') {
    .el-range-editor.el-input__inner {
      background: rgba(2, 22, 47, 0.8);
      border: 1px solid #00acfc;
      border-radius: 4px;
      color: #ffffff;

      ::v-deep .el-input__icon,
      ::v-deep .el-range-separator {
        color: #fff;
      }

      ::v-deep .el-range-input {
        background-color: transparent;
        color: #ffffff;
      }
    }

    .shortcuts-item {
      background: rgba(3, 16, 33, 0.85);
      border: 1px solid #00acfc;
      color: #fff;

      &.is-active {
        border-image: linear-gradient(0deg, #ffdd68, #7b6312) 10 10;
        background: linear-gradient(0deg, #908611 0%, #3e3506 100%);
        border: 1px solid #ffdd68 !important;
      }
    }
  }
}

.time-range-picker {
  width: 100%;
  display: flex;

  &--medium {
    .shortcuts-item {
      @include pickerSizeMixin('medium');
    }
  }

  &--small {
    .shortcuts-item {
      @include pickerSizeMixin('small');
    }
  }

  &--mini {
    .shortcuts-item {
      @include pickerSizeMixin('mini');
    }
  }

  &--light {
    @include pickerEffectMixin('light');
  }

  &--dark {
    @include pickerEffectMixin('dark');
  }

  .el-range-editor {
    flex: 1;

    &.el-input__inner {
      padding-right: 0;
    }
  }

  .shortcuts-wrap {
    display: flex;
    border-radius: 4px;
    margin-left: 12px;

    .shortcuts-item {
      font-weight: 400;
      text-align: center;
      cursor: pointer;

      &:first-child {
        border-top-left-radius: 4px;
        border-bottom-left-radius: 4px;
      }

      &:last-child {
        border-top-right-radius: 4px;
        border-bottom-right-radius: 4px;
      }
    }

    .shortcuts-item + .shortcuts-item {
      border-left-color: transparent;
    }
  }
}
</style>
